package cloud.keveri.gateway.sdk;

import cloud.keveri.base.utils.NetUtils;
import cloud.keveri.gateway.dto.ServiceDefinition;
import cloud.keveri.gateway.dto.ServiceInstance;
import cloud.keveri.gateway.dto.invoker.ServiceInvoker;
import cloud.keveri.gateway.sdk.annotation.GatewayService;
import cloud.keveri.gateway.sdk.annotation.Invoker;
import cloud.keveri.gateway.sdk.shutdown.ShutdownHookManager;
import cloud.keveri.registry.api.InstanceRegisterRepository;
import cloud.keveri.gateway.dto.KeveriClientConfigure;
import cn.hutool.core.bean.BeanUtil;
import lombok.NonNull;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.ProviderConfig;
import org.apache.dubbo.config.spring.ServiceBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static cloud.keveri.gateway.sdk.def.Const.DEFAULT_WEIGHT;
import static org.apache.dubbo.common.BaseServiceMetadata.COLON_SEPARATOR;

/**
 * 网关服务扫描
 * <p>
 * 作者：沈自在
 **/
@Slf4j
@SuppressWarnings("all")
public class GatewayServiceDiscovery extends AbstractServiceDiscoverySupport<ServiceBean>
        implements EnvironmentAware {

    private Environment environment;

    @Override
    public void setEnvironment(@NonNull Environment environment) {
        this.environment = environment;
    }

    public GatewayServiceDiscovery(KeveriClientConfigure configure, InstanceRegisterRepository register) {
        super(configure, register);
    }

    @Override
    protected Map<String, ServiceBean> acquireTargetBean(final ApplicationContext context) {
        return context.getBeansOfType(ServiceBean.class);
    }

    @Override
    protected void processRegistry(ApplicationContext context, ServiceBean service) {
        // 1. 排除非网关暴露服务
        Object bean = service.getRef();
        Class<?> clazz = bean.getClass();
        if (!clazz.isAnnotationPresent(GatewayService.class)) {
            return;
        }

        // 2. 解析定义
        GatewayService annotation = clazz.getAnnotation(GatewayService.class);

        String serviceId = annotation.serviceId();
        Protocol protocol = annotation.protocol();
        String patternPath = annotation.patternPath();
        String version = annotation.version();

        // 3. 解析 Invoker
        Map<String, ServiceInvoker> invokerMap = new HashMap<>();
        for (Method method : clazz.getMethods()) {
            Invoker invoker = method.getAnnotation(Invoker.class);
            if (Objects.isNull(invoker)) continue;

            String path = invoker.path();
            boolean auth = invoker.auth();

            // 提取参数数组
            Class<?>[] parameterTypesClazz = method.getParameterTypes();
            String parameterTypes = Arrays
                    .stream(parameterTypesClazz)
                    .map(Class::getName)
                    .collect(Collectors.joining(","));


            // 处理超时时间
            Integer timeout = service.getTimeout();
            if (timeout == null || timeout.intValue() == 0) {
                ProviderConfig providerConfig = service.getProvider();
                if (providerConfig != null) {
                    Integer providerTimeout = providerConfig.getTimeout();
                    if (providerTimeout == null || providerTimeout.intValue() == 0) {
                        timeout = 5000;
                    } else {
                        timeout = providerTimeout;
                    }
                }
            }

            // 构造 invoker
            ServiceInvoker serviceInvoker = ServiceInvoker.builder()
                    .invokerPath(path)
                    .auth(auth)
                    .interfaceClazz(service.getInterface())
                    .methodName(method.getName())
                    .parameterTypes(parameterTypes)
                    .timeout(timeout)
                    .version(service.getVersion())
                    .build();

            // 处理版本号
            String dubboVersion = serviceInvoker.getVersion();
            if (!StringUtils.isBlank(dubboVersion)) {
                version = dubboVersion;
            }
            invokerMap.put(path, serviceInvoker);
        }

        // 4. 构造 ServiceDefinition
        ServiceDefinition definition = ServiceDefinition.builder()
                .uniqueId(serviceId + ":" + version)
                .serviceId(serviceId)
                .version(version)
                .protocol(protocol.getProtocol())
                .patternPath(patternPath)
                .enable(true)
                .invokerMap(invokerMap)
                .contextPath(contextPath)
                .build();

        // 5. 构建服务实例
        String localIp = NetUtils.getLocalIp();
        String port = environment.getProperty("dubbo.protocol.port");


        ServiceInstance instance = ServiceInstance.builder()
                .serviceInstanceId(localIp + COLON_SEPARATOR + port)
                //.serviceInstanceId("mta26b.natappfree.cc")
                .uniqueId(definition.getUniqueId())
                .ip(localIp)
                .port(Integer.parseInt(port))
                .registerTime(System.currentTimeMillis())
                .version(version)
                .weight(DEFAULT_WEIGHT)
                .build();

        // 6, 注册
        register(definition, instance);

        // 7. shutdown 回调
        ShutdownHookManager.get().addShutdownHook(new Thread(() -> {
            log.info("[Keveri-Cloud] 网关 SDK 注销钩子触发");
            final ServiceDefinition offlineServiceDefinition = BeanUtil.copyProperties(definition, ServiceDefinition.class);
            final ServiceInstance offlineServiceInstant = BeanUtil.copyProperties(instance, ServiceInstance.class);
            getRegister().deregister(offlineServiceDefinition, offlineServiceInstant);
        }), 2);
    }
}
